#ifndef HANDLEFACTORY_HANDLEFACTORY_H
#define HANDLEFACTORY_HANDLEFACTORY_H

///////////////////////////////////////////////////////////////////////////////
//
// HandleFactory.h
//
///////////////////////////////////////////////////////////////////////////////
//
// Author:            Jan Maes
// Created:           Sunday, June 15, 2008
// Responsibilities:  Creates handles to objects of template type TObject
// Usage:             Let your object class publicly inherit from HandleFactory 
//                    and make HandleFactory a friend of your object class.
// Examples:
//                    Example 1: create a HandleFactory::Handle
//                    -----------------------------------------
//                    class MyObject : HandleFactory::HandleFactory<MyObject>
//                      {
//                      private:
//                        friend class HandleFactory::HandleFactory<MyObject>;
//                        MyObject(){}
//                        ~MyObject(){}
//                      };
//
//                    int main()
//                      {
//                      MyObject::THandleType p_object = MyObject::Create();
//                      return 0;
//                      }
//
//                    Example 2: create a boost::shared_ptr
//                    -------------------------------------
//                    class MyObject : HandleFactory::HandleFactory<MyObject, boost::shared_ptr<MyObjectAsSharedPtr>>
//                      {
//                      private:
//                        friend class HandleFactory::HandleFactory<MyObject, boost::shared_ptr<MyObjectAsSharedPtr>>;
//                        MyObject(){}
//                        ~MyObject(){}
//                      };
//
//                    int main()
//                      {
//                      MyObject::THandleType p_object = MyObject::Create();
//                      return 0;
//                      }
//
//                    Example 3: create a std::auto_ptr
//                    -------------------------------------
//                    class MyObject : HandleFactory::HandleFactory<MyObject, std::auto_ptr<MyObjectAsSharedPtr>, true>
//                      {
//                      private:
//                        friend class HandleFactory::HandleFactory<MyObject, std::auto_ptr<MyObjectAsSharedPtr>, true>;
//                        MyObject(){}
//                      public:
//                        ~MyObject(){}
//                      };
//
//                    int main()
//                      {
//                      MyObject::THandleType p_object = MyObject::Create();
//                      return 0;
//                      }
//
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Includes
///////////////////////////////////////////////////////////////////////////////

#include "HandleFactoryObjectCreatorImpl.h"
#include "Handle.h"

#include <memory>

///////////////////////////////////////////////////////////////////////////////
// namespace HandleFactory
///////////////////////////////////////////////////////////////////////////////

namespace HandleFactory
  {

  ///////////////////////////////////////////////////////////////////////////////
  // class HandleFactory
  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle = Handle<TObject>, bool TPublicDestructor = false>
  class HandleFactory
    {
    public:
      typedef THandle THandleType;
      typedef const THandle THandleTypeConst;

    protected:

      HandleFactory();

      virtual ~HandleFactory() throw();

    public:

      static THandle Create();

      template <class TParameter_1>
      static THandle Create(TParameter_1 i_parameter_1);

      template <class TParameter_1, 
                class TParameter_2>
      static THandle Create(TParameter_1 i_parameter_1, 
                            TParameter_2 i_paramater_2);

      template <class TParameter_1, 
                class TParameter_2,
                class TParameter_3>
      static THandle Create(TParameter_1 i_parameter_1, 
                            TParameter_2 i_paramater_2,
                            TParameter_3 i_parameter_3);

      template <class TParameter_1, 
                class TParameter_2,
                class TParameter_3,
                class TParameter_4>
      static THandle Create(TParameter_1 i_parameter_1, 
                            TParameter_2 i_paramater_2,
                            TParameter_3 i_parameter_3,
                            TParameter_4 i_parameter_4);

      template <class TParameter_1, 
                class TParameter_2,
                class TParameter_3,
                class TParameter_4,
                class TParameter_5>
      static THandle Create(TParameter_1 i_parameter_1, 
                            TParameter_2 i_paramater_2,
                            TParameter_3 i_parameter_3,
                            TParameter_4 i_parameter_4,
                            TParameter_5 i_parameter_5);

     static const THandle CreateConst();

      template <class TParameter_1>
      static const THandle CreateConst(TParameter_1 i_parameter_1);

      template <class TParameter_1, 
                class TParameter_2>
      static const THandle CreateConst(TParameter_1 i_parameter_1, 
                                       TParameter_2 i_paramater_2);

      template <class TParameter_1, 
                class TParameter_2,
                class TParameter_3>
      static const THandle CreateConst(TParameter_1 i_parameter_1, 
                                       TParameter_2 i_paramater_2,
                                       TParameter_3 i_parameter_3);

      template <class TParameter_1, 
                class TParameter_2,
                class TParameter_3,
                class TParameter_4>
      static const THandle CreateConst(TParameter_1 i_parameter_1, 
                                       TParameter_2 i_paramater_2,
                                       TParameter_3 i_parameter_3,
                                       TParameter_4 i_parameter_4);

      template <class TParameter_1, 
                class TParameter_2,
                class TParameter_3,
                class TParameter_4,
                class TParameter_5>
      static const THandle CreateConst(TParameter_1 i_parameter_1, 
                                       TParameter_2 i_paramater_2,
                                       TParameter_3 i_parameter_3,
                                       TParameter_4 i_parameter_4,
                                       TParameter_5 i_parameter_5);

    private:

      static void _DeleteObject(TObject* ip_object) throw();

      struct _Deleter
        {
        void operator () (TObject* ip_object) const throw();
        };
    };

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  HandleFactory<TObject, THandle, TPublicDestructor>::HandleFactory()
    {
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  HandleFactory<TObject, THandle, TPublicDestructor>::~HandleFactory()
    {
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  THandle HandleFactory<TObject, THandle, TPublicDestructor>::Create()
    {        
    return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::Create<_Deleter>(new TObject());
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1>
  THandle HandleFactory<TObject, THandle, TPublicDestructor>::Create(TParameter_1 i_parameter_1)
    {
    return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::Create<_Deleter>(new TObject(i_parameter_1));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2>
  THandle HandleFactory<TObject, THandle, TPublicDestructor>::Create(TParameter_1 i_parameter_1, 
                                                                     TParameter_2 i_parameter_2)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::Create<_Deleter>(new TObject(i_parameter_1, 
                                                                                                            i_parameter_2));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2,
            class TParameter_3>
  THandle HandleFactory<TObject, THandle, TPublicDestructor>::Create(TParameter_1 i_parameter_1, 
                                                                     TParameter_2 i_parameter_2,
                                                                     TParameter_3 i_parameter_3)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::Create<_Deleter>(new TObject(i_parameter_1, 
                                                                                                            i_parameter_2,
                                                                                                            i_parameter_3));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2,
            class TParameter_3,
            class TParameter_4>
  THandle HandleFactory<TObject, THandle, TPublicDestructor>::Create(TParameter_1 i_parameter_1, 
                                                                     TParameter_2 i_parameter_2,
                                                                     TParameter_3 i_parameter_3,
                                                                     TParameter_4 i_parameter_4)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::Create<_Deleter>(new TObject(i_parameter_1, 
                                                                                                            i_parameter_2,
                                                                                                            i_parameter_3,
                                                                                                            i_parameter_4));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2,
            class TParameter_3,
            class TParameter_4,
            class TParameter_5>
  THandle HandleFactory<TObject, THandle, TPublicDestructor>::Create(TParameter_1 i_parameter_1, 
                                                                     TParameter_2 i_parameter_2,
                                                                     TParameter_3 i_parameter_3,
                                                                     TParameter_4 i_parameter_4,
                                                                     TParameter_5 i_parameter_5)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::Create<_Deleter>(new TObject(i_parameter_1, 
                                                                                                            i_parameter_2,
                                                                                                            i_parameter_3,
                                                                                                            i_parameter_4,
                                                                                                            i_parameter_5));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  const THandle HandleFactory<TObject, THandle, TPublicDestructor>::CreateConst()
    {        
    return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::CreateConst<_Deleter>(new TObject());
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1>
  const THandle HandleFactory<TObject, THandle, TPublicDestructor>::CreateConst(TParameter_1 i_parameter_1)
    {
    return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::CreateConst<_Deleter>(new TObject(i_parameter_1));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2>
  const THandle HandleFactory<TObject, THandle, TPublicDestructor>::CreateConst(TParameter_1 i_parameter_1, 
                                                                                TParameter_2 i_parameter_2)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::CreateConst<_Deleter>(new TObject(i_parameter_1, 
                                                                                                                 i_parameter_2));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2,
            class TParameter_3>
  const THandle HandleFactory<TObject, THandle, TPublicDestructor>::CreateConst(TParameter_1 i_parameter_1, 
                                                                                TParameter_2 i_parameter_2,
                                                                                TParameter_3 i_parameter_3)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::CreateConst<_Deleter>(new TObject(i_parameter_1, 
                                                                                                                 i_parameter_2,
                                                                                                                 i_parameter_3));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2,
            class TParameter_3,
            class TParameter_4>
  const THandle HandleFactory<TObject, THandle, TPublicDestructor>::CreateConst(TParameter_1 i_parameter_1, 
                                                                                TParameter_2 i_parameter_2,
                                                                                TParameter_3 i_parameter_3,
                                                                                TParameter_4 i_parameter_4)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::CreateConst<_Deleter>(new TObject(i_parameter_1, 
                                                                                                                 i_parameter_2,
                                                                                                                 i_parameter_3,
                                                                                                                 i_parameter_4));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  template <class TParameter_1, 
            class TParameter_2,
            class TParameter_3,
            class TParameter_4,
            class TParameter_5>
  const THandle HandleFactory<TObject, THandle, TPublicDestructor>::CreateConst(TParameter_1 i_parameter_1, 
                                                                                TParameter_2 i_parameter_2,
                                                                                TParameter_3 i_parameter_3,
                                                                                TParameter_4 i_parameter_4,
                                                                                TParameter_5 i_parameter_5)
    {
   return HandleFactoryObjectCreatorImpl<TObject, THandle, TPublicDestructor>::CreateConst<_Deleter>(new TObject(i_parameter_1, 
                                                                                                                 i_parameter_2,
                                                                                                                 i_parameter_3,
                                                                                                                 i_parameter_4,
                                                                                                                 i_parameter_5));
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  void HandleFactory<TObject, THandle, TPublicDestructor>::_DeleteObject(TObject* ip_object)
    {
    delete ip_object;
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject, class THandle, bool TPublicDestructor>
  void HandleFactory<TObject, THandle, TPublicDestructor>::_Deleter::operator () (TObject* ip_object) const
    {
    _DeleteObject(ip_object);
    }

  ///////////////////////////////////////////////////////////////////////////////

  } // namespace HandleFactory

#endif